package com.ctrip.cap.monitor.common;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ctrip.cap.device.DeviceStore;
import com.ctrip.cap.device.android.AndroidDevice;
import com.ctrip.cap.device.android.impl.DefaultDeviceStore;
import com.ctrip.cap.monitor.screenshot.ScreenShot;
import com.ctrip.cap.monitor.utils.LogUtil;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

public class DeviceManager {

	private static final ScreenShot sScreenShot = new ScreenShot();
	private static ServerSocket ss = null;

	protected static final DeviceStore<AndroidDevice> deviceStore = DefaultDeviceStore
			.getInstance();
	protected static Set<AndroidDevice> devices = null;
	private static String PortReg = new String("[^\\d]+([\\d\\.]+):("
			+ Constants.SERVER_PORT
			+ ")\\s+[\\d\\.]+:\\d{1,}\\s+(\\w+)*[^\\d]+(\\d+)");

	private static Set<String> snSet = Collections
			.synchronizedSet(new HashSet<String>());

	static final Logger logger = LoggerFactory.getLogger(DeviceManager.class);

	private DeviceManager() {

	}

	public static void start() {
		// Release Port
		ReleasePort();

		LogUtil.info("-------monitor start--------");

		devices = deviceStore.getDevices();

		synchronized (devices) {
			Iterator<AndroidDevice> it = devices.iterator();

			while (it.hasNext()) {
				AndroidDevice tempDevice = it.next();
				synchronized (sScreenShot) {
					sScreenShot.onConnected(tempDevice,
							new AtomicBoolean(false));
				}
			}
		}

		startServer();
	}

	public static void stop() {
		logger.info("stopping monitor ");
		IOUtils.closeQuietly(ss);

		Iterator<String> iter = snSet.iterator();
		while (iter.hasNext()) {
			String sn = iter.next();
			synchronized (sScreenShot) {
				sScreenShot.onDisconnected(sn);
			}
		}

		logger.info("stop monitor successed");
	}

	private static void ReleasePort() {
		int tmpPort, pid;
		boolean flag;
		try {
			String result;
			try {
				result = readConsole("cmd /c netstat -ano | findstr "
						+ Constants.SERVER_PORT);

				String[] lines = result.split(System
						.getProperty("line.separator"));
				Pattern p = Pattern.compile(PortReg);

				for (String str : lines) {
					Matcher m = p.matcher(str);
					if (!m.find()) {
						continue;
					}
					flag = true;
					String IP = m.group(1);
					String Port = m.group(2);
					String status = m.group(3);
					String Pid = m.group(4);
					LogUtil.info(String.format(
							"IP : %s, Port: %s, status : %s, Pid : %s", IP,
							Port, status, Pid));

					tmpPort = Integer.parseInt(m.group(2).toString());
					pid = Integer.parseInt(m.group(4).toString());

					if ((pid == Constants.SERVER_PORT && tmpPort != Constants.SERVER_PORT)
							|| pid == 0) {
						continue;
					}

					try {
						Process process = Runtime.getRuntime().exec(
								String.format("taskkill /f /pid %s", pid));
						process.waitFor();
						LogUtil.info("kill process pid:" + pid);
					} catch (InterruptedException e) {
						flag = false;
						LogUtil.info(String.format("Monitor : \n %s", e));
					}

					LogUtil.info(String.format("Monitor : kill %s %s.",
							m.group(4), flag ? "Success" : "Failure"));
				}

			} catch (InterruptedException e) {
				LogUtil.error(e);
			}

		} catch (IOException e) {
			LogUtil.error(e);
		}

	}

	private static String readConsole(String cmd) throws IOException,
			InterruptedException {
		StringBuffer cmdout = new StringBuffer();
		LogUtil.info("Excute Command：" + cmd);
		Process process = Runtime.getRuntime().exec(cmd);

		InputStream fis = process.getInputStream();
		BufferedReader br = new BufferedReader(
				new InputStreamReader(fis, "gbk"));
		String line = null;

		while ((line = br.readLine()) != null) {
			cmdout.append(line).append(System.getProperty("line.separator"));
		}
		process.waitFor();

		return cmdout.toString().trim();
	}

	private static void startServer() {
		new Thread(new Runnable() {

			@Override
			public void run() {

				Socket socket = null;
				try {

					ss = new ServerSocket(Constants.SERVER_PORT);
					while (true && !ss.isClosed()) {
						socket = ss.accept();

						BufferedInputStream bis = new BufferedInputStream(
								socket.getInputStream());
						ByteArrayOutputStream baos = new ByteArrayOutputStream();
						int len = -1;
						byte[] buff = new byte[1024];
						while ((len = bis.read(buff)) != -1) {
							baos.write(buff, 0, len);
						}
						String jsonStr = new String(baos.toByteArray());
						printLog(socket, jsonStr);

						JsonElement json = new JsonParser().parse(jsonStr);
						String sn = json.getAsJsonObject().get(Constants.UDID)
								.getAsString();
						setDeviceEnable(sn, true);
						snSet.add(sn);
					}

				} catch (IOException e) {
					LogUtil.info("--------monitor  stop!----------");
				} finally {
					IOUtils.closeQuietly(ss);
					IOUtils.closeQuietly(socket);
				}
			}
		}).start();
	}

	private static void printLog(Socket socket, String msg) {
		InetAddress address = socket.getInetAddress();
		StringBuilder sb = new StringBuilder();
		sb.append("Host    :\t" + address.getHostName());
		sb.append(Constants.ENTER);
		sb.append("IP      :\t");
		sb.append(address.getHostAddress());
		sb.append(Constants.ENTER);
		sb.append("Port    :\t");
		sb.append(socket.getPort());
		sb.append(Constants.ENTER);
		sb.append("Message :\t" + msg);
		LogUtil.info(sb.toString());
	}

	private static void setDeviceEnable(String sn, boolean flag) {
		synchronized (sScreenShot) {
			sScreenShot.setDeviceEnable(sn, flag);
		}
	}

	public interface DeviceConnectStateListener {
		public void onDisconnected(final String sn);

		public void onConnected(final AndroidDevice device,
				AtomicBoolean isStart);
	}
}
